#include "Die.h"
#include "LocationCursorForMember.h"

#include <cstdio>

#include <libdwarf/dwarf.h>

namespace dwarf {

LocationCursorForMember::LocationCursorForMember(const void *_objectAddress,
												 Dwarf_Debug _debug,
												 Dwarf_Die _memberDie)
		: objectAddress_(_objectAddress),
		  debug_(_debug),
		  memberDie_(_memberDie) {
}

LocationCursorForMember::~LocationCursorForMember() {
}

Location LocationCursorForMember::fetch(Dwarf_Unsigned _byteSize) {
	RaiiPointer<Dwarf_Attribute> attribute(debug_);
	int rtn = Die::getAttribute(memberDie_, DW_AT_data_member_location, attribute);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return Location(Location::TYPE_ERROR);
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
		return Location(Location::TYPE_END);
	}
	Dwarf_Half version = 0;
	Dwarf_Half offsetSize = 0;
	rtn = dwarf_get_version_of_die(memberDie_, &version, &offsetSize);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return Location(Location::TYPE_ERROR);
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
		return Location(Location::TYPE_END);
	}
	Dwarf_Error error;
	Dwarf_Half attributeFormat;
	rtn = dwarf_whatform(attribute.getPointer(), &attributeFormat, &error);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return Location(Location::TYPE_ERROR);
	}
	else if (rtn == DW_DLV_NO_ENTRY) {
		return Location(Location::TYPE_END);
	}
	if (dwarf_get_form_class(version, DW_AT_data_member_location, offsetSize, attributeFormat) != DW_FORM_CLASS_CONSTANT) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return Location(Location::TYPE_ERROR);
	}
	Dwarf_Unsigned u;
	rtn = dwarf_formudata(attribute.getPointer(), &u, &error);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return Location(Location::TYPE_ERROR);
	}
	else if (rtn == DW_DLV_OK) {
		return Location(Location::TYPE_ADDRESS, (char *) objectAddress_ + u, _byteSize);
	}
	Dwarf_Signed s;
	rtn = dwarf_formsdata(attribute.getPointer(), &s, &error);
	if (rtn == DW_DLV_ERROR) {
		fprintf(stderr, "[ERR] (%s, %d) %s\n", __FILE__, __LINE__, __FUNCTION__);
		return Location(Location::TYPE_ERROR);
	}
	else if (rtn == DW_DLV_OK) {
		return Location(Location::TYPE_ADDRESS, (char *) objectAddress_ + s, _byteSize);
	}
	return Location(Location::TYPE_END);
}

}
